package  
{
	import Box2D.Collision.b2AABB;
	import Box2D.Collision.Shapes.b2CircleDef;
	import Box2D.Collision.Shapes.b2PolygonDef;
	import Box2D.Collision.Shapes.b2Shape;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2BodyDef;
	import Box2D.Dynamics.b2World;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.StatusEvent;
	import flash.events.TimerEvent;
	import flash.net.SharedObject;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.utils.Timer;
	/**
	 * ...
	 * @author ElviS
	 */
	public class BallsGame extends GameScreen 
	{
		//Основное тело. Использован физический движок Box2d.
		private var _world:b2World;
		private const  RATIO:Number = 30; //необходимая константа для пересчета в B2d
		
		private var _sprites:Vector.<Sprite> = new Vector.<Sprite>();
		private var _bodies:Vector.<b2Body> = new Vector.<b2Body>();
		
		/*private var ballTimer:Timer;*/ //на случай, если нужно будет усложнить задачу и добавить больше мячей на сцену по таймеру.
		private var playTimer:Timer;
		
		private var batSprite:Sprite; //ракетки игрока и компьютера, объявляются их графические и физические сущности.
		private var batBody:b2Body;
		private var batAiSprite:Sprite;
		private var batAiBody:b2Body;
		
		private var playTime:String; //Сколько времени удалось продержаться.
		private var enemyCount:uint; //Сколько забил компьютер
		private var playerCount:uint; //Сколько забил игрок
		private var num:uint = 0;
		private var playerWin:Boolean; 
		private var maxCount:uint = 5; //До скольки ведется счет перед выводом результатов
		private var scoreSharedObject:SharedObject = SharedObject.getLocal("myGameCount"); //Объект для сохранения результатов не завершенной игры.
		
		private var enemyCountText:TextField = new TextField(); //Оформление счета
		private var playerCountText:TextField = new TextField();
		private var countTextFormat:TextFormat = new TextFormat("Arial", 32, 0xff0f0f, true);
		
		public function BallsGame() 
		{
			box2dInit();
			
			createWalls(); // Создание ограничителей (стен)
			
			playerCount = scoreSharedObject.data.player_score; //возвращаем сохраненные результаты
			enemyCount = scoreSharedObject.data.enemy_score;
			
			setText(); //располагаем счет на сцене
			
			createBall(randomInt(20,380), randomInt(20, 430)); //создаем игровой мяч
			
			createBat(STAGE_WIDTH - 10, STAGE_HEIGTH / 20, 5, STAGE_HEIGTH / 10, 0x00ff00, false); // создание ракеток
			createBat(10, STAGE_HEIGTH / 20, 5, STAGE_HEIGTH / 10, 0xffff00, true);
			
			/*ballTimer = new Timer(15000, 0);*/
			playTimer = new Timer(1000, 0); // создаем таймер подсчета времени в игре
			
			drawShape(0x0000ff, 0, 0, STAGE_WIDTH, STAGE_HEIGTH);// бэкграунд
			drawShape(0x00ff00, STAGE_WIDTH * .5, 0, 1, STAGE_HEIGTH); //разделительная полоса
		}
		
		private function drawShape(color:Number, _x:int, _y:int, _width:uint, _heigth:uint):void
		{
			this.graphics.beginFill(color, 1); 
			this.graphics.drawRect(_x, _y , _width, _heigth);
			this.graphics.endFill();
		}
		
		private function setText():void 
		{
			addChild(enemyCountText);
			addChild(playerCountText);
			
			enemyCountText.setTextFormat(countTextFormat);
			enemyCountText.defaultTextFormat = countTextFormat;
			playerCountText.setTextFormat(countTextFormat);
			playerCountText.defaultTextFormat = countTextFormat;
			
			enemyCountText.autoSize = "right";
			enemyCountText.selectable = false;
			playerCountText.autoSize = "left";
			playerCountText.selectable = false;
			
			enemyCountText.text = String(enemyCount);
			playerCountText.text = String(playerCount);
			
			enemyCountText.x = STAGE_WIDTH * .5 - enemyCountText.width - 10;
			enemyCountText.y = playerCountText.y = enemyCountText.height + 10;
			playerCountText.x = STAGE_WIDTH * .5 + 10;
		}
		
		private function rememberScoreHandler():void //функция сохранения счета
		{
			removeEventListener(Event.REMOVED_FROM_STAGE, rememberScoreHandler);
			scoreSharedObject.data.player_score = playerCount;
			scoreSharedObject.data.enemy_score = enemyCount;
			scoreSharedObject.data.time = playTimer.currentCount;
			scoreSharedObject.flush();
		}
		
		private function randomInt(min:int, max:int):int
		{
			if (min > max) throw new Error("Min value is bigger than max... Ooops");
			var value:int = Math.random() * max;
			if (value < min) value = min + min * Math.random();
			
			return value;
		}
		
		private function createBat(x:int, y:int, w:uint, h:uint, color:Number, typeAi:Boolean):void //создание ракеток
		{
			var shapeDef:b2PolygonDef = new b2PolygonDef();
			shapeDef.restitution = 1.2; //упругость может быть изменена, чем она больше, тем больше ускорение мяча после удара
			shapeDef.friction = 0;
			shapeDef.SetAsBox(w / RATIO, h / RATIO);
			
			var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.position.Set(x / RATIO, y / RATIO);
			// создание физического тела объекта.
			if (typeAi)
			{
				batAiBody = _world.CreateBody(bodyDef);
				var shapeAi:b2Shape = batAiBody.CreateShape(shapeDef);
				batAiBody.SetMassFromShapes();
			}
			else 
			{
				batBody = _world.CreateBody(bodyDef);
				var shape:b2Shape = batBody.CreateShape(shapeDef);
				batBody.SetMassFromShapes();
			}
			
			(typeAi)?shapeAi.SetUserData("ENEMY"):shape.SetUserData("PLAYER"); //это для проверки обо что ударился мяч, в последствии может быть доработано для проверки столкновений с различными объектами.
			
			var sprite:Sprite = new Sprite();
			sprite.graphics.beginFill(color, 1);
			sprite.graphics.drawRect(x - w, y - h, w * 2, h * 2);
			sprite.graphics.endFill();
			
			if (typeAi)
			{
				batAiSprite = sprite;
				addChild(batAiSprite);
			}
			else 
			{
				batSprite = sprite;
				addChild(batSprite);
			}
		}
		
		private function createBall(x:int, y:int):void //создаем мяч
		{
			var shapeDef:b2CircleDef = new b2CircleDef();
			shapeDef.radius = 15 / RATIO;
			shapeDef.density = 1;
			shapeDef.friction = 0;
			shapeDef.restitution = .5;
			
			var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.position.Set(x / RATIO, y / RATIO);
			
			var body:b2Body = _world.CreateBody(bodyDef);
			var shape:b2Shape = body.CreateShape(shapeDef);
			body.SetMassFromShapes();
			
			shape.SetUserData("BALL");
			
			var power:uint = 10;
			body.ApplyImpulse(new b2Vec2((playerWin)?-5:5, Math.sin(randomNumber(0, Math.PI * 2)) * power), new b2Vec2(body.GetWorldCenter().x, body.GetWorldCenter().y));//его начальный вектор ускорения
			power++;
			
			var sprite:Sprite = new GameBall(); //графика
			
			sprite.x = x;
			sprite.y = y;
			
			addChild(sprite);
			
			_sprites.push(sprite);
			_bodies.push(body);
		}
		
		private function randomNumber(min:Number, max:Number):Number 
		{
			if (min > max) throw new Error("Error! Min !> max");
			var value:Number = Math.random() * max;
			if (value < min) value = min * Math.random() * min;
			return value;
		}
		
		private function createWalls():void //создадим стены
		{
			createBox(STAGE_WIDTH * .5, -5, STAGE_WIDTH * .5, 5);
			createBox(STAGE_WIDTH * .5, STAGE_HEIGTH, STAGE_WIDTH * .5, 5);
			//createBox( -5, STAGE_HEIGTH*.5, 5, STAGE_HEIGTH*.5); //эти значения могут быть использованы для создания боковых стенок
			//createBox( STAGE_WIDTH + 5,  STAGE_HEIGTH*.5, 5,  STAGE_HEIGTH*.5); 
		}
		
		private function createBox(x:int, y:int, w:uint, h:uint):void 
		{
			var shapeDef:b2PolygonDef = new b2PolygonDef();
			shapeDef.density = 0; //тело с абсолютной массой (не подвижное)
			shapeDef.restitution = 1; //упругость
			shapeDef.SetAsBox(w / RATIO, h / RATIO);
			
			var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.position.Set(x / RATIO, y / RATIO);
			
			var body:b2Body = _world.CreateBody(bodyDef);
			body.CreateShape(shapeDef);
			body.SetMassFromShapes();
			
			var sprite:Sprite = new Sprite();
			sprite.graphics.beginFill(0x000000, 1);
			sprite.graphics.drawRect(x - w, y - h, w * 2, h * 2);
			sprite.graphics.endFill();
			
			addChild(sprite);
		}
		
		private function box2dInit():void 
		{
			var aabb:b2AABB = new b2AABB();
			aabb.lowerBound.Set( -1000/RATIO, -1000/RATIO);
			aabb.upperBound.Set(1000/RATIO, 1000/RATIO);
			var gravity:b2Vec2 = new b2Vec2(0, 0);
			
			_world = new b2World(aabb, gravity, false);
			_world.SetContactListener(new myContactListener()); //слушатель пересечения объектов. Здесь не используеться, но может быть использован в будущем.
		}
		
		override public function start():void
		{
			addEventListener(Event.ENTER_FRAME, onLive);
			playTimer.start();
			/*ballTimer.addEventListener(TimerEvent.TIMER, onTimer);
			ballTimer.start();*/
		}
		
		/*private function onTimer(e:TimerEvent):void //если захотим создавать ещё один мяч через определенный интервал
		{
			ballTimer.delay += 3000;
			createBall(randomInt(20,380), randomInt(20, 430));
		}*/
		
		override public function stop():void 
		{
			removeEventListener(Event.ENTER_FRAME, onLive);
			playTimer.stop();
			/*ballTimer.removeEventListener(TimerEvent.TIMER, onTimer);
			ballTimer.stop();*/
		}
		
		private function onLive(e:Event):void 
		{
			_world.Step(1 / 60, 10);
			
			if (mouseY > 0 && mouseY < STAGE_HEIGTH * .9) // движение ракетки игрока
			{
				batBody.SetXForm(new b2Vec2((STAGE_WIDTH - 10) / RATIO, mouseY / RATIO), 0);
				batSprite.y = batBody.GetPosition().y * RATIO;
			}
			
			for (var i:uint = 0; i < _sprites.length; i++) // координаты мяча.
			{
				_sprites[i].x = _bodies[i].GetPosition().x * RATIO;
				_sprites[i].y = _bodies[i].GetPosition().y * RATIO;
			}
			
			batAiBody.SetXForm(new b2Vec2(0, _sprites[num].y / RATIO), 0); //движение ракетки компьютера
			batAiSprite.y = batAiBody.GetPosition().y * RATIO;
			
			if (_sprites[num].x > STAGE_WIDTH) //проверка зачисления очков
			{
				enemyCount ++;
				removeChild(_sprites[num]);
				num++;
				playerWin = false;
				createBall(randomInt(20, 380), randomInt(20, 430));
				rememberScoreHandler();
				enemyCountText.text = String(enemyCount);
			}
			else if (_sprites[num].x < 0)
			{
				playerCount ++;
				removeChild(_sprites[num]);
				num++;
				playerWin = true;
				createBall(randomInt(20, 380), randomInt(20, 430));
				rememberScoreHandler();
				playerCountText.text = String(playerCount);
			}
			
			if (enemyCount > (maxCount-1) || playerCount > (maxCount-1)) // вывод результатов. добавил от себя подсчет времени.
			{
				playTime = scoreSharedObject.data.time + playTimer.currentCount + " <font color='#000'>сек</font> \n" + "<font color='#000'>Счет: (</font>" + enemyCount + "<font color='#000'>,</font>" + playerCount +"<font color='#000'>).</font>";
				dispatchEvent(new StatusEvent(StatusEvent.STATUS, false, false, playTime, "gotoGameInfo"));
				scoreSharedObject.clear();
			}
		}
	}

}